#pragma once

#include "Event.h"
#include "CriticalSection.h"
#include <queue>

namespace GPUCC
{
	template<typename Type>
	class MultiThreadedQueue
	{
	public:
		MultiThreadedQueue() : m_NotEmpty(false, true)
		{
		}

		~MultiThreadedQueue()
		{
		}

		void Push(const Type& Value)
		{
			this->m_Lock.Enter();

			this->m_Queue.push(Value);

			if (this->m_Queue.size() == 1)
			{
				this->m_NotEmpty.Set();
			}

			this->m_Lock.Leave();
		}

		Type Pop()
		{
			this->m_Lock.Enter();

			while (this->m_Queue.empty())
			{
				this->m_Lock.Leave();

				this->m_NotEmpty.Wait();

				this->m_Lock.Enter();
			}

			Type value = this->m_Queue.front();
			this->m_Queue.pop();

			this->m_Lock.Leave();

			return (value);
		}

		bool Empty()
		{
			bool Result;

			this->m_Lock.Enter();
			Result = this->m_Queue.empty();
			this->m_Lock.Leave();

			return (Result);
		}

		unsigned int Size()
		{
			unsigned int Result;

			this->m_Lock.Enter();
			Result = this->m_Queue.size();
			this->m_Lock.Leave();

			return (Result);
		}

	private:
		std::queue<Type> m_Queue;
		CPU::Event m_NotEmpty;
		CPU::CriticalSection m_Lock;
	};
}